package com.company;


import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.util.*;

/**
 * @date : 2021/11/27 16:44
 * 各种内存区域OOM模拟
 */

public class P002_OutOfMemory {

    /**
     * -XX:+UseParNewGC
     * -XX:+UseConcMarkSweepGC
     * -XX:MetaspaceSize=10M
     * -XX:MaxMetaspaceSize=10M
     * -XX:+PrintGCDetails
     * -Xloggc:gc.log
     * -XX:+HeapDumpOnOutOfMemoryError
     * -XX:HeapDumpPath=./
     */
    public static class MetaspaceOOM {
        // 使用 -XX:MetaspaceSize=10M -XX:MaxMetaspaceSize=10M 参数设置最大元数据区可以产生元数据区OOM
        // java.lang.OutOfMemoryError: Metaspace
        // -XX:PermSize=10M -XX:MaxPermSize=10M 可测试jdk1.7永久代溢出
        public static void main(String[] args) {
            int count = 0;
            try {
                for (; ; ) {
                    Enhancer enhancer = new Enhancer();
                    enhancer.setSuperclass(OOMObject.class);
                    enhancer.setUseCache(false);
                    enhancer.setCallback(new MethodInterceptor() {
                        @Override
                        public Object intercept(Object o, Method method, Object[] objects,
                                                MethodProxy methodProxy) throws Throwable {
                            return methodProxy.invokeSuper(o, objects);
                        }
                    });
                    enhancer.create();
                    count++;
                }
            } catch (Throwable e) {
                System.out.println("目前创建了 " + ++count + " 个OOMObject的动态类");
                e.printStackTrace();
            }
        }

        static class OOMObject {

        }
    }

    public static class DirectMemoryOOM {
        // 使用 -XX:MaxDirectMemorySize=5M 参数设置直接内存最大值 可以产生直接内存OOM
        // java.lang.OutOfMemoryError: Direct buffer memory
        public static void main(String[] args) {
            // jdk1.8包的内容
            // System.out.println("直接内存：" + sun.misc.VM.maxDirectMemory() / 1024 / 1024 + "MB");
            // 编译时添加参数
            //         --add-exports java.base/jdk.internal.misc=ALL-UNNAMED
            // 运行时添加参数
            //         --add-opens java.base/jdk.internal.misc=ALL-UNNAMED
            // 链接：https://www.jianshu.com/p/e3a82bae68b2
            // System.out.println("直接内存：" + jdk.internal.misc.VM.maxDirectMemory() / 1024 / 1024 + "MB");
            ByteBuffer byteBuffer = ByteBuffer.allocateDirect(6 * 1024 * 1024);
        }
    }

    public static class HeapOOM {

        static class OOMObject {
        }

        // VM Args：-Xms10m -Xmx10m
        // java.lang.OutOfMemoryError: Java heap space
        public static void main(String[] args) {
            List<OOMObject> list = new ArrayList<>();
            int count = 0;
            try {
                while (true) {
                    list.add(new OOMObject());
                    count++;
                }
            } catch (Throwable e) {
                System.out.println("创建OOMObject类的个数" + count);
                e.printStackTrace();
            }
        }
    }

    /**
     * 字符串常量池所在区域验证
     * JDK1.8字符串常量池是存放在堆区，而不是元数据区，设置元数据区的大小，看是否会有元数据区的OOM
     * -XX:MetaspaceSize=10M -XX:MaxMetaspaceSize=10M -XX:+PrintGCDetails
     * -Xms100m -Xmx100m
     * 设置堆内存大小，堆空间会产生OOM
     * java.lang.OutOfMemoryError: GC overhead limit exceeded
     */
    public static class ConstantPoolOOM {
        static long i = 0;

        public static void main(String[] args) {
            List<String> strings = new LinkedList<>();
            for (; ; ) {
                String s = "" + i++;
                s = s.intern();
                strings.add(s);
                if (i % 1000 == 0) {
                    // 让 minor GC 回收堆内存空间
                    strings = new ArrayList<>();
                }
            }
        }
    }


    public static class ArraySizeExceedsVMLimit {
        // java.lang.OutOfMemoryError: Requested array size exceeds VM limit
        public static void main(String[] args) {
            int[] nums = new int[Integer.MAX_VALUE];
        }
    }

    public static class GCOverheadLimitExceeded {
        // -Xmx100m
        // java.lang.OutOfMemoryError: GC overhead limit exceeded
        public static void main(String[] args) throws Exception {
            Map map = System.getProperties();
            Random r = new Random();
            while (true) {
                map.put(r.nextInt(), "value");
            }
        }
    }

}
